#pragma once

#include <iostream>
#include <queue>
#include <cstdlib>
#include <unistd.h>
#include <pthread.h>

using namespace std;


template <class T>
class BlockQueue
{
public:
    BlockQueue(uint32_t cap = 5) : cap_(cap)
    {
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&concond_, nullptr);
        pthread_cond_init(&procond_, nullptr);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&concond_);
        pthread_cond_destroy(&procond_);
    }

public:
    //生产接口
    void push(const T &in)
    {
        // 加锁
        // 如果队列已满,阻塞，等待被唤醒
        // 如果未满,push队列，唤醒消费者
        // 解锁
        pthread_mutex_lock(&mutex_);
        while (isFull())
        {
            pthread_cond_wait(&procond_, &mutex_);
        }
        bq_.push(in);
        pthread_mutex_unlock(&mutex_);
        pthread_cond_signal(&concond_);
    }

    T pop()
    {
        // 加锁
        // 如果为空，阻塞，等待被唤醒
        // 如果未空，pop队列
        // 解锁
        pthread_mutex_lock(&mutex_);
        while (isEmpty())
        {
            pthread_cond_wait(&concond_, &mutex_);
        }
        T tmp = bq_.front();
        bq_.pop();
        pthread_mutex_unlock(&mutex_);
        pthread_cond_signal(&procond_);
        return tmp;
    }

private:
    bool isEmpty()
    {
        return bq_.empty();
    }
    bool isFull()
    {
        return bq_.size() == cap_;
    }


private:
    uint32_t cap_;           //容量
    queue<T> bq_;            // blockqueue
    pthread_mutex_t mutex_;  //保护阻塞队列的互斥锁
    pthread_cond_t concond_; // 让消费者等待的条件变量
    pthread_cond_t procond_; // 让生产者等待的条件变量
};